package org.edu.common;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;

import javax.annotation.Resource;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@SuppressWarnings("unchecked")
@Transactional
public abstract class BaseDao<T> implements DAO<T> {
	protected HibernateTemplate hibernateTemplate;
	private Class<T> entityClass = GenericsUtils.getSuperClassGenricType(this.getClass());
	private String entityName = getEntityName(entityClass);
	
	@Resource
	public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
		this.hibernateTemplate = hibernateTemplate;
	}
	
	public HibernateTemplate getHibernateTemplate() {
		return this.hibernateTemplate;
	}
	
	public void save(T entity) {
		hibernateTemplate.saveOrUpdate(entity);
		hibernateTemplate.flush();
	} 
	
	public void delete(Serializable...ids) {
		for (Serializable id : ids) {
			hibernateTemplate.delete(hibernateTemplate.load(entityClass, id));
		}
	}
	@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
	public T find(Serializable id) {
		return (T) hibernateTemplate.get(entityClass, id);
	}
	
	public QueryResult<T> getQueryResult() {
		return getQueryResult(-1,-1, null,null,null);
	}
	
	public QueryResult<T> getQueryResult(int firstResult, int maxResult,
			LinkedHashMap<String, String> orderByMap) {
		return getQueryResult(firstResult, maxResult, null,null,orderByMap);
	}
	
	@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
	public QueryResult<T> getQueryResult(final int firstResult,final int maxResult,
			final String condition,final Object[] params,
			final LinkedHashMap<String, String> orderByMap) {
		QueryResult<T> ret = new QueryResult<T>();
		
		List list = hibernateTemplate.executeFind(new HibernateCallback() {
			public List doInHibernate(Session ses) throws HibernateException,
					SQLException {
				Query query = ses.createQuery(buildHQL(condition,orderByMap));
				if(firstResult != -1 && maxResult != -1) {
					query.setFirstResult(firstResult)
					.setMaxResults(maxResult);
				}
				setParams(query, params);
				return query.list();
			}
		});
		ret.setResultList(list);
		
		Long totalRecord = (Long)hibernateTemplate.execute(new HibernateCallback(){
			public Object doInHibernate(Session ses)
					throws HibernateException, SQLException {
				Query query = ses.createQuery(buildHQL(condition));
				setParams(query, params);
				return (Long)query.uniqueResult();
			}
			
		});
		ret.setTotalrecord(totalRecord);
		return ret;
	}
	public QueryResult<T> getQueryResult(int firstResult, int maxResult,
			String condition, Object[] params) {
		return getQueryResult(firstResult, maxResult, condition, params,null);
	}
	public QueryResult<T> getQueryResult(int firstResult, int maxResult) {
		return getQueryResult(firstResult, maxResult,null,null,null);
	}
	/**
	 * 设置参数
	 * @param query
	 * @param params
	 */
	protected void setParams(Query query,Object[] params) {
		if(query != null && params != null && params.length > 0) {
			for (int i = 0; i < params.length; i++) {
				query.setParameter(i,params[i]);
			}
		}
	}
	/**
	 * 创建hql语句
	 * @param condition
	 * @param params
	 * @param orderByMap
	 * @return hql语句
	 */
	protected String buildHQL(String condition,LinkedHashMap<String,String> orderByMap) {
		StringBuilder builder = new StringBuilder("FROM ")
		.append(entityName)
		.append(" o ");
		
		if(condition != null && !"".equals(condition.trim())) {
			builder.append("WHERE ")
			.append(condition);
		}		
		if(orderByMap != null){
			if(!orderByMap.isEmpty()) {
				builder.append(" ORDER BY ");
				for (String key : orderByMap.keySet()) {
					builder.append("o.")
					.append(key)
					.append(' ')
					.append(orderByMap.get(key));
				}
			}
		}
		
		return builder.toString();
	}
	protected String buildHQL(String condition) {
		StringBuilder builder = new StringBuilder("SELECT COUNT(")
		.append(getCountField(entityClass))
		.append(") FROM ")
		.append(entityName)
		.append(" o ");
		
		if(condition != null && !"".equals(condition.trim())) {
			builder.append("where ")
			.append(condition);
		}
		return builder.toString();
	}
	/**
	 * 取得实体名
	 * @param entityClass
	 * @return 实体名
	 */
	protected String getEntityName(Class<T> entityClass) {
		String name = entityClass.getSimpleName();
		Entity entity = entityClass.getAnnotation(Entity.class);
		if(entity != null && !"".equals(entity.name().trim())) {
			name = entity.name();
		}
		return name;
	}
	/**
	 * 获取统计属性,该方法是为了解决hibernate解析联合主键select count(o) from Xxx o语句BUG而增加,sql为select count(field1,field2,...),显示使用count()统计多个字段是错误的
	 * @param <E>
	 * @param clazz
	 * @return
	 */
	protected static <E> String getCountField(Class<E> clazz){
		String out = "o";
		try {
			PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(clazz).getPropertyDescriptors();
			for(PropertyDescriptor propertydesc : propertyDescriptors){
				Method method = propertydesc.getReadMethod();
				if(method!=null && method.isAnnotationPresent(EmbeddedId.class)){					
					PropertyDescriptor[] ps = Introspector.getBeanInfo(propertydesc.getPropertyType()).getPropertyDescriptors();
					out = "o."+ propertydesc.getName()+ "." + (!ps[1].getName().equals("class")? ps[1].getName(): ps[0].getName());
					break;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
        return out;
	}
	
}
